/*
	Copyright (c) 2013 MeherTJ G. All rights reserved.
	License: LGPL for personnal study or free software.
*/

#pragma once

/**
	 class Exception extends std::exception
		with extra info: FunctionName, FileName, Line.

	 use __zen_must(cond), __zen_must_else(cond, msg), __zen_throw(msg)
*/

#include <exception>
#include <stdlib.h>
#include <string>
#include <typeinfo>

namespace Zen
{
  class Exception : public std::exception
  {
protected:
    std::string mWhat{};
    std::string mFile{};
    std::string mFunc{};
    int mLine{};

public:
    Exception(std::string whats, std::string const& func, std::string const& file, int lineNo) noexcept
      : mWhat(whats)
      , mFile(file)
      , mFunc(func)
      , mLine(lineNo)
    {
    }
    Exception(Exception const& e) noexcept = default;
    Exception(Exception&& e) noexcept
    {
      *this = std::move(e);
    }
    Exception& operator=(Exception&& e) noexcept
    {
      mWhat = e.mWhat;
      mFile = e.mFile;
      mFunc = e.mFunc;
      mLine = e.mLine;
      return *this;
    }
    virtual char const* what() const noexcept override
    {
      return mWhat.data();
    }
    std::string FunctionName() const noexcept
    {
      return mFunc;
    }
    std::string FileName() const noexcept
    {
      return mFile;
    }
    int Line() const noexcept
    {
      return mLine;
    }
  };
} // namespace Zen

#define __FILENAME__ Zen::PickFileName(__FILE__)

#define __zen_must(cond)                                                              \
  do                                                                                  \
  {                                                                                   \
    if (!(cond)) throw(Zen::Exception{ "failed", __FUNCTION__, __FILE__, __LINE__ }); \
  } while (0)

#define __zen_must_else(cond, msg)                                               \
  do                                                                             \
  {                                                                              \
    if (!(cond)) throw(Zen::Exception{ msg, __FUNCTION__, __FILE__, __LINE__ }); \
  } while (0)

#define __zen_throw(msg) throw(Zen::Exception{ msg, __FUNCTION__, __FILE__, __LINE__ })
